// Thud - a dynamic object-oriented language
// Copyright (C) 2009,2010 David H. Hovemeyer <david.hovemeyer@gmail.com>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

/*
 * ConstantPool.cpp
 *
 *  Created on: Nov 24, 2009
 *      Author: dhovemey
 */

#include <cstdio>
#include <cassert>
#include "util/DataInputStream.h"
#include "util/DataOutputStream.h"
#include "vm/ConstantString.h"
#include "vm/ConstantSymbol.h"
#include "vm/ConstantClass.h"
#include "vm/ConstantInt32.h"
#include "vm/ConstantBoolean.h"
#include "vm/Magic.h"
#include "vm/ClassFormatException.h"
#include "vm/ConstantPool.h"

//#define DEBUG_LOAD(args...) printf(args)
#define DEBUG_LOAD(args...)

ConstantPool::ConstantPool()
	: m_constantMap()
	, m_frozen(false)
{
}

ConstantPool::~ConstantPool()
{

}

uint16_t ConstantPool::addAndAdopt(Constant *c)
{
	assert(!m_frozen);

	// see if an identical constant already exists
	ConstantMap::iterator i = m_constantMap.find(c);

	uint16_t index;

	if (i == m_constantMap.end()) {
		// constant being added for first time
		index = m_pool.size();
		m_pool.push_back(c);
		m_constantMap[c] = index;
	} else {
		// an identical constant already exists; re-use
		index = (*i).second;
		delete c;
	}

	return index;
}

Constant *ConstantPool::getConstant(uint16_t cpIndex)
{
	assert(cpIndex < m_pool.size());
	return m_pool[cpIndex];
}

uint16_t ConstantPool::createConstantString(const std::wstring &str)
{
	return addAndAdopt(new ConstantString(str));
}

uint16_t ConstantPool::createConstantSymbol(const std::wstring &symName)
{
	return addAndAdopt(new ConstantSymbol(createConstantString(symName)));
}

uint16_t ConstantPool::createConstantClass(const std::wstring &clsName)
{
	return addAndAdopt(new ConstantClass(createConstantString(clsName)));
}

uint16_t ConstantPool::createConstantInt32(int32_t value)
{
	return addAndAdopt(new ConstantInt32(value));
}

uint16_t ConstantPool::createConstantBoolean(bool value)
{
	return addAndAdopt(new ConstantBoolean(value));
}

void ConstantPool::freeze()
{
	m_frozen = true;
	m_constantMap.clear();
}

void ConstantPool::read(DataInputStream *in)
{
	uint32_t numConstants = in->readInt();
	if (numConstants > CLASS_MAX_CONSTANTS) {
		throw ClassFormatException(L"Constant pool has too many constants");
	}

	DEBUG_LOAD("reading %lu constants\n", (unsigned long) numConstants);

	for (uint32_t i = 0; i < numConstants; i++) {
		Constant* c = Constant::readConstant(in);
		m_pool.push_back(c);
	}

	freeze();
}

void ConstantPool::write(DataOutputStream *out) const
{
	out->writeInt((uint32_t) m_pool.size());

	for (ConstantList::const_iterator i = m_pool.begin(); i != m_pool.end(); i++) {
		Constant::writeConstant(out, *i);
	}

	DEBUG_LOAD("wrote %lu constants\n", (unsigned long) m_pool.size());
}
